<template>
	<div class="page">
		<h1>百度 > 语音识别</h1>

		<h3>1. 获取 token</h3>
		<p>请输入你应用的 client_id 和 client_secret 获取 access_token</p>
		<p style="font-weight: bold; color: orange">access_token: {{ auth }}</p>

		<el-input v-model.trim="clientId" placeholder="client_id ( 应用的API Key )" />
		<el-input v-model.trim="clientSecret" placeholder="client_secret ( 应用的 Secret Key )" />
		<el-button type="primary" @click="handleGetAccessToken">获取AccessToken</el-button>

		<h3>2. 录音</h3>
		<el-button type="danger" @click="handleAudioStart">开始录音</el-button>
		<el-button @click="handleAudioStop">停止录音</el-button>

		<br />

		<video v-if="audioSrc" controls :src="audioSrc"></video>

		<h3>3. 语音识别</h3>
		<el-button type="default" @click="submitWav">上传 wav 音频识别</el-button>
		<el-button type="success" @click="submitPcm">(推荐) 上传 pcm 音频识别</el-button>
	</div>
</template>

<script>
import axios from 'axios'
import qs from 'qs'
import Recorder from '@/utils/recorder'
import { pcmBlobToBase64 } from '@/utils/recorder/pcm-extend'

console.log('🍉  Recorder:', Recorder)

export default {
	name: 'Baidu',
	data() {
		return {
			clientId: '5N39iuKBteVVb9eMVhFhdfhP',
			clientSecret: 'yBHJ45StgB6R5CWQt0j012IwGDfVjzoD',

			rec: null,
			pcmBlob: '',
			wavBlob: '',
			audioSrc: '',
		}
	},
	created() {},
	computed: {
		auth() {
			return localStorage.getItem('access_token')
		},
	},
	methods: {
		openMsg(message, type) {
			console.log('openMsg', ...arguments)
			this.$message({ showClose: true, message, type })
		},
		// 1. 获取AccessToken
		async handleGetAccessToken() {
			try {
				const option = {
					grant_type: 'client_credentials',
					client_id: this.clientId,
					client_secret: this.clientSecret,
				}

				const res = await axios.post('/baidu/oauth', qs.stringify(option))
				console.log('oauth res:', res)

				if (res.status !== 200 || res.data === '') {
					return this.openMsg('获取失败', 'warning')
				}

				this.openMsg('获取 token 成功', 'success')
				localStorage.setItem('access_token', res.data.access_token)

				this.clientId = ''
				this.clientSecret = ''
			} catch (error) {
				console.log('oauth err', error)
			}
		},
		handleAudioStart() {
			// 定义录音对象 > .pcm 格式
			const rec = (this.rec = Recorder({
				type: 'pcm', // 可以定义其他格式
				bitRate: 16,
				sampleRate: 16000,
			}))

			// 开始录音 (会判断浏览器是否存在录音权限)
			rec.open(function () {
				rec.start()
				console.log('开始录音')
			})
		},

		handleAudioStop() {
			// 释放录音资源，当然可以不释放，后面可以连续调用start
			// 但不释放时系统或浏览器会一直提示在录音，最佳操作是录完就close掉
			const close = () => {
				this.rec?.close()
				this.rec = null
			}

			this.rec.stop(
				(blob, duration) => {
					console.log('录音结束 pcm blob:', blob)
					this.pcmBlob = blob
					close()

					Recorder.pcm2wav(
						{ sampleRate: 16000, bitRate: 16, blob: blob },
						wavBlob => {
							console.log('生成 wav:', wavBlob)
							this.wavBlob = wavBlob
							this.audioSrc = (window.URL || webkitURL).createObjectURL(wavBlob)
						},
						function (msg) {
							console.log(msg)
						}
					)
				},
				errMsg => {
					console.log('录音失败: ' + errMsg)
					close()
				}
			)
		},

		// wav 转 base64 上传得到识别结果
		async submitWav() {
			const token = localStorage.getItem('access_token')
			if (!token) return openMsg('请先获取 token !', 'warning')
			if (!this.pcmBlob) return this.openMsg('请先完成录音', 'warning')

			// wav 转 base64
			let blobToDataURL = (blob, callback) => {
				var a = new FileReader()
				a.onload = function (e) {
					callback(e.target.result.split('data:audio/wav;base64,')[1])
				}
				a.readAsDataURL(blob)
			}

			const wavBlob = this.wavBlob
			blobToDataURL(wavBlob, async base_64 => {
				const param = {
					speech: base_64, // 本地语音文件的的二进制语音数据 ，需要进行base64 编码。与len参数连一起使用。
					len: wavBlob.size, // 字节数
					dev_pid: 1537, // 普通话识别代码
					cuid: '541b:3f:5af4:b2c9',
					rate: 16000, // 音频格式16k或8k 采样率、16bit 位深、单声道，
					token: token, // 根据你的参数获取的token
					channel: 1, // 单声道
					format: 'wav', // 识别的格式
				}

				console.log('wav param', param)

				const res = await axios.post('/baidu/audio2text', param, {
					headers: { 'Content-Type': 'application/json' },
				})

				if (res.data.err_no !== 0) {
					return this.openMsg(res.data.err_msg, 'warning')
				}

				this.openMsg('wav 识别成功', 'success')
				console.log('wav 识别结果：', res, res.data.result)
			})
		},

		// (官方推荐上传 pcm) pcm 转 base64 上传得到识别结果
		async submitPcm() {
			try {
				const token = localStorage.getItem('access_token')
				if (!token) return openMsg('请先获取 token !', 'warning')
				if (!this.pcmBlob) return this.openMsg('请先完成录音', 'warning')

				const pcmBlob = this.pcmBlob
				const pcmBase64 = await pcmBlobToBase64(this.pcmBlob)
				console.log('🍉  pcmBase64:', pcmBase64)

				const param = {
					speech: pcmBase64, // 本地语音文件的的二进制语音数据 ，需要进行 base64 编码。与len参数连一起使用。
					len: pcmBlob.size, // 字节数
					dev_pid: 1537, // 普通话识别代码
					cuid: '541b:3f:5af4:b2c9',
					rate: 16000, // 音频格式16k或8k 采样率、16bit 位深、单声道，
					token: token, // 根据你的参数获取的token
					channel: 1, // 单声道
					format: 'pcm', // 识别的格式
				}

				console.log('pcm param', param)
				const res = await axios.post('/baidu/audio2text', param, {
					headers: { 'Content-Type': 'application/json' },
				})

				if (res.data.err_no !== 0) {
					return this.openMsg(res.data.err_msg, 'warning')
				}

				this.openMsg('pcm 识别成功', 'success')
				console.log('pcm 识别结果：', res, res.data.result)
			} catch (err) {
				console.log('submitPcm err', err)
			}
		},
	},
}
</script>

<style rel="stylesheet/scss" lang="scss" scoped>
.page {
	padding: 0 15px;

	.el-input {
		width: 300px;
		margin-right: 10px;
	}
}
</style>
